﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;

namespace iMagistic
{
    public class ResizingAdorner : Adorner
    {
        // Resizing adorner uses Thumbs for visual elements.  
        // The Thumbs have built-in mouse input handling.
        Thumb topLeft, topRight, bottomLeft, bottomRight;
        UIElement myCanvas;

        // To store and manage the adorner's visual children.
        VisualCollection visualChildren;

        // Initialize the ResizingAdorner.
        public ResizingAdorner(UIElement adornedElement, UIElement canvas)
            : base(adornedElement)
        {
            visualChildren = new VisualCollection(this);
            myCanvas = canvas;

            // Call a helper method to initialize the Thumbs
            // with a customized cursors.
            BuildAdornerCorner(ref topLeft, Cursors.SizeNWSE, new Thickness(50, 50, 0, 0));
            BuildAdornerCorner(ref topRight, Cursors.SizeNESW, new Thickness(0, 50, 50, 0));
            BuildAdornerCorner(ref bottomLeft, Cursors.SizeNESW, new Thickness(50, 0, 0, 50));
            BuildAdornerCorner(ref bottomRight, Cursors.SizeNWSE, new Thickness(0, 0, 50, 50));

            // Add handlers for resizing.
            bottomLeft.DragDelta += new DragDeltaEventHandler(HandleBottomLeft);
            bottomRight.DragDelta += new DragDeltaEventHandler(HandleBottomRight);
            topLeft.DragDelta += new DragDeltaEventHandler(HandleTopLeft);
            topRight.DragDelta += new DragDeltaEventHandler(HandleTopRight);
        }

        void HideThumbs()
        {

            topLeft.Width = 0;
            topLeft.Height = 0;
            topRight.Width = 0;
            topRight.Height = 0;

            bottomLeft.Width = 0;
            bottomLeft.Height = 0;

            bottomRight.Width = 0;
            bottomRight.Height = 0;

        }

        // Handler for resizing from the bottom-right.
        void HandleBottomRight(object sender, DragDeltaEventArgs args)
        {
            #region Initialization
            // getting handler for the adorned element
            FrameworkElement croppingRectangle = this.AdornedElement as FrameworkElement;
            Thumb hitThumb = sender as Thumb;

            if (croppingRectangle == null || hitThumb == null) return;
            FrameworkElement parentElement = croppingRectangle.Parent as FrameworkElement;

            // Ensure that the Width and Height are properly initialized after the resize.
            EnforceSize(croppingRectangle);

            // Change the size by the amount the user drags the mouse, as long as it's larger 
            // than the width or height of an adorner, respectively.
            #endregion
            HideThumbs();
            #region Computing and setting width
            // saving the old value of width
            double width_old = croppingRectangle.Width;

            // computing the new value of width, minimal width will be corner thumbnail width
            double width_new = Math.Max(croppingRectangle.Width + args.HorizontalChange, hitThumb.DesiredSize.Width);

            // if the cropping rectangle exceeds the frame of canvas, then reset the width
            if ((Canvas.GetLeft(croppingRectangle) + width_new) > myCanvas.RenderSize.Width)
                croppingRectangle.Width = width_old;
            else
            {
                // if the cropping rectangle is inside the canvas, then assign the new width, else assign default max value
                if (myCanvas.RenderSize.Width - width_new >= 0)
                    croppingRectangle.Width = width_new;
                else
                    croppingRectangle.Width = myCanvas.RenderSize.Width;
            }
            #endregion

            #region Computing and setting height
            // saving the old value of height
            double height_old = croppingRectangle.Height;

            // computing the new value of height, minimal width will be corner thumbnail height
            double height_new = Math.Max(croppingRectangle.Height + args.VerticalChange, hitThumb.DesiredSize.Height);

            // if the cropping rectangle exceeds the frame of canvas, then reset the height
            if ((Canvas.GetTop(croppingRectangle) + height_new) > myCanvas.RenderSize.Height)
            {
                croppingRectangle.Height = height_old;
            }
            else
            {   // if the cropping rectangle is inside the canvas, then assign the new height, else assign default max value
                if (myCanvas.RenderSize.Height - height_new >= 0)
                    croppingRectangle.Height = height_new;
                else
                    croppingRectangle.Height = myCanvas.RenderSize.Height;
            }
            #endregion
        }

        // Handler for resizing from the top-right.
        void HandleTopRight(object sender, DragDeltaEventArgs args)
        {
            #region Initialization
            // getting handler for the adorned element
            FrameworkElement croppingRectangle = this.AdornedElement as FrameworkElement;
            Thumb hitThumb = sender as Thumb;

            if (croppingRectangle == null || hitThumb == null) return;
            FrameworkElement parentElement = croppingRectangle.Parent as FrameworkElement;

            // Ensure that the Width and Height are properly initialized after the resize.
            EnforceSize(croppingRectangle);
            #endregion
            HideThumbs();
            // Change the size by the amount the user drags the mouse, as long as it's larger 
            // than the width or height of an adorner, respectively.

            #region Computing and saving the width
            // saving the old value of width
            double width_old = croppingRectangle.Width;

            // computing the new value of width, minimal width will be corner thumbnail width
            double width_new = Math.Max(croppingRectangle.Width + args.HorizontalChange, hitThumb.DesiredSize.Width);

            // if the cropping rectangle exceeds the frame of canvas, then reset the width
            if ((Canvas.GetLeft(croppingRectangle) + width_new) > myCanvas.RenderSize.Width)
                croppingRectangle.Width = width_old;
            else
            {
                // if the cropping rectangle is inside the canvas, then assign the new width, else assign default max value
                if (myCanvas.RenderSize.Width - width_new >= 0)
                    croppingRectangle.Width = width_new;
                else croppingRectangle.Width = myCanvas.RenderSize.Width;
            }
            #endregion

            #region Computing and setting the height

            // saving the old value of height
            double height_old = croppingRectangle.Height;
            // computing the new value of height, minimal width will be corner thumbnail height
            double height_new = Math.Max(croppingRectangle.Height - args.VerticalChange, hitThumb.DesiredSize.Height);

            #endregion

            #region Adjusting the top position of the croppingrectangle
            // saving the old value
            double top_old = Canvas.GetTop(croppingRectangle);
            // Computing the new top position
            double top_new = top_old - (height_new - height_old);
            // if the new position is negative, clip the croppingRectange to top border and resume the old height, else assign the new height
            if (top_new < 0)
            {
                top_new = 0;
                croppingRectangle.Height = height_old;
            }
            else
            {
                croppingRectangle.Height = height_new;
            }
            // Setting the top position acordingly
            Canvas.SetTop(croppingRectangle, top_new);
            #endregion

        }

        // Handler for resizing from the top-left.
        void HandleTopLeft(object sender, DragDeltaEventArgs args)
        {
            #region Initialization
            FrameworkElement croppingRectangle = AdornedElement as FrameworkElement;
            Thumb hitThumb = sender as Thumb;

            if (croppingRectangle == null || hitThumb == null) return;

            // Ensure that the Width and Height are properly initialized after the resize.
            EnforceSize(croppingRectangle);
            #endregion
            HideThumbs();
            // Change the size by the amount the user drags the mouse, as long as it's larger 
            // than the width or height of an adorner, respectively.

            // adjusting width of the adorned element
            #region Computing  the new width
            // saving the old value
            double width_old = croppingRectangle.Width;
            // computing the new value of width, minimal width will be corner thumbnail width
            double width_new = Math.Max(croppingRectangle.Width - args.HorizontalChange, hitThumb.DesiredSize.Width);
            // if the width exceeds the canvas size, assign the default max width value
            if (width_new > myCanvas.RenderSize.Width) width_new = myCanvas.RenderSize.Width;
            #endregion

            #region Adjusting the left position and setting the width of the cropping rectangle
            // saving the old value
            double left_old = Canvas.GetLeft(croppingRectangle);
            // computing the new left position
            double left_new = left_old - (width_new - width_old);

            // handling the case when the new left position is negative, 
            // clip croppingRectangle to left border of canvas, and resume it's original width
            // else asigh the new width and left position
            if (left_new < 0)
            {
                left_new = 0;
                croppingRectangle.Width = width_old;
            }
            else
            {
                croppingRectangle.Width = width_new;
            }

            Canvas.SetLeft(croppingRectangle, left_new);
            #endregion

            #region Adjusting the top position and height of the adorned element
            //saving the old height value
            double height_old = croppingRectangle.Height;
            // computing the new height
            double height_new = Math.Max(croppingRectangle.Height - args.VerticalChange, hitThumb.DesiredSize.Height);

            // handling the case when new height exceeds canvas height
            if (height_new > myCanvas.RenderSize.Height) height_new = myCanvas.RenderSize.Height;

            // adjusting top position of the adorned element
            double top_old = Canvas.GetTop(croppingRectangle);
            double top_new = top_old - (height_new - height_old);

            // handling the case when the new top position is negative, 
            // clip croppingRectangle to top border of canvas, and resume it's original height
            // else asigh the new height and top position
            if (top_new < 0)
            {
                top_new = 0;
                croppingRectangle.Height = height_old;
            }
            else
            {
                croppingRectangle.Height = height_new;
            }
            Canvas.SetTop(croppingRectangle, top_new);
            #endregion
        }

        // Handler for resizing from the bottom-left.
        void HandleBottomLeft(object sender, DragDeltaEventArgs args)
        {
            #region Initialization
            FrameworkElement croppingRectangle = AdornedElement as FrameworkElement;
            Thumb hitThumb = sender as Thumb;

            if (croppingRectangle == null || hitThumb == null) return;

            // Ensure that the Width and Height are properly initialized after the resize.
            EnforceSize(croppingRectangle);
            #endregion
            HideThumbs();
            // Change the size by the amount the user drags the mouse, as long as it's larger 
            // than the width or height of an adorner, respectively.

            #region Computing the new Width
            // saving the old value of width
            double width_old = croppingRectangle.Width;
            // computing the new value of width, minimal width will be the corner thumb width
            double width_new = Math.Max(croppingRectangle.Width - args.HorizontalChange, hitThumb.DesiredSize.Width);
            #endregion

            #region Adjusting the left position and setting the width of the cropping rectangle
            // save old value
            double left_old = Canvas.GetLeft(croppingRectangle);
            // compute the new value
            double left_new = left_old - (width_new - width_old);
            // handling the case when the new left position is negative, 
            // clip croppingRectangle to left border of canvas, and resume it's original width
            // else asigh the new width and left position
            if (left_new < 0)
            {
                left_new = 0;
                croppingRectangle.Width = width_old;
            }
            else
            {
                croppingRectangle.Width = width_new;
            }
            Canvas.SetLeft(croppingRectangle, left_new);
            #endregion

            #region Adjusting the top position and height of the cropping rectangle

            // saving the old value
            double height_old = croppingRectangle.Height;
            // computing the new height
            double height_new = Math.Max(croppingRectangle.Height + args.VerticalChange, hitThumb.DesiredSize.Height);

            // handling the case when cropping Rectangle height exceds the canvas height, 
            if ((Canvas.GetTop(croppingRectangle) + height_new) > myCanvas.RenderSize.Height)
            {
                // resuming original width of the cropping Rectangle
                croppingRectangle.Height = height_old;
            }
            else
            {   // if the cropping rectangle(considerning the new heigh) is inside the canvas  - assighn it the new heigh
                // else assign the default size 
                if (myCanvas.RenderSize.Height - height_new >= 0)
                    croppingRectangle.Height = height_new;
                else croppingRectangle.Height = myCanvas.RenderSize.Height;
            }
            #endregion
        }

        private void MoveThumb(Thumb thumb, Point position)
        {
            var element = AdornedElement as FrameworkElement;
            position = new Point(position.X * element.ActualWidth, position.Y * element.ActualHeight);

            var halfSize = (Vector)thumb.DesiredSize / 2;
            thumb.Arrange(new Rect(position - halfSize, position + halfSize));
        }

        // Arrange the Adorners.
        protected override Size ArrangeOverride(Size finalSize)
        {
            // desiredWidth and desiredHeight are the width and height of the element that's being adorned.  
            // These will be used to place the ResizingAdorner at the corners of the adorned element.  
            double desiredWidth = AdornedElement.DesiredSize.Width;
            double desiredHeight = AdornedElement.DesiredSize.Height;
            // adornerWidth & adornerHeight are used for placement as well.
            double adornerWidth = this.DesiredSize.Width;
            double adornerHeight = this.DesiredSize.Height;

            topLeft.Arrange(new Rect(-adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
            topRight.Arrange(new Rect(desiredWidth - adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
            bottomLeft.Arrange(new Rect(-adornerWidth / 2, desiredHeight - adornerHeight / 2, adornerWidth, adornerHeight));
            bottomRight.Arrange(new Rect(desiredWidth - adornerWidth / 2, desiredHeight - adornerHeight / 2, adornerWidth, adornerHeight));

            // Return the final size.
            return finalSize;
        }

        // Helper method to instantiate the corner Thumbs, set the Cursor property, 
        // set some appearance properties, and add the elements to the visual tree.
        void BuildAdornerCorner(ref Thumb cornerThumb, Cursor customizedCursor, Thickness margin)
        {
            if (cornerThumb != null) return;

            cornerThumb = new Thumb();

            // Set some arbitrary visual characteristics.
            cornerThumb.Cursor = customizedCursor;
            cornerThumb.Height = cornerThumb.Width = 50;
            cornerThumb.Opacity = 0.70;
            cornerThumb.Background = new SolidColorBrush(Colors.Gray);

            cornerThumb.Margin = margin;
            visualChildren.Add(cornerThumb);
        }

        // This method ensures that the Widths and Heights are initialized.  Sizing to content produces
        // Width and Height values of Double.NaN.  Because this Adorner explicitly resizes, the Width and Height
        // need to be set first.  It also sets the maximum size of the adorned element.
        void EnforceSize(FrameworkElement adornedElement)
        {
            if (adornedElement.Width.Equals(Double.NaN))
                adornedElement.Width = adornedElement.DesiredSize.Width;
            if (adornedElement.Height.Equals(Double.NaN))
                adornedElement.Height = adornedElement.DesiredSize.Height;

            FrameworkElement parent = adornedElement.Parent as FrameworkElement;
            if (parent != null)
            {
                adornedElement.MaxHeight = parent.ActualHeight;
                adornedElement.MaxWidth = parent.ActualWidth;
            }
        }
        // Override the VisualChildrenCount and GetVisualChild properties to interface with 
        // the adorner's visual collection.
        protected override int VisualChildrenCount { get { return visualChildren.Count; } }
        protected override Visual GetVisualChild(int index) { return visualChildren[index]; }

        protected override void OnRender(DrawingContext drawingContext)
        {
            var pen = new Pen(Brushes.WhiteSmoke, 1.2);
            pen.DashStyle = DashStyles.Dash;

            drawingContext.DrawLine(pen, new Point(this.AdornedElement.RenderSize.Width / 3, 0), new Point(this.AdornedElement.RenderSize.Width / 3, this.AdornedElement.RenderSize.Height));

            drawingContext.DrawLine(pen, new Point(this.AdornedElement.RenderSize.Width * 2 / 3, 0), new Point(this.AdornedElement.RenderSize.Width * 2 / 3, this.AdornedElement.RenderSize.Height));

            drawingContext.DrawLine(pen, new Point(0, this.AdornedElement.RenderSize.Height / 3), new Point(this.AdornedElement.RenderSize.Width, this.AdornedElement.RenderSize.Height / 3));

            drawingContext.DrawLine(pen, new Point(0, this.AdornedElement.RenderSize.Height *2 / 3), new Point(this.AdornedElement.RenderSize.Width, this.AdornedElement.RenderSize.Height *2 / 3));
            double Length; 

            if(this.AdornedElement.RenderSize.Width> this.AdornedElement.RenderSize.Height)
                Length  = this.AdornedElement.RenderSize.Height / 6;
            else
                Length  = this.AdornedElement.RenderSize.Width / 6;

            double PositionX = this.AdornedElement.RenderSize.Width / 2 - Length/2;
            double PositionY = this.AdornedElement.RenderSize.Height / 2 - Length/2;

            var pen2 = new Pen(Brushes.White, 1.5);
            pen2.DashStyle = DashStyles.Solid;

            drawingContext.DrawLine(pen2, new Point(PositionX, this.AdornedElement.RenderSize.Height / 2), new Point(PositionX + Length, this.AdornedElement.RenderSize.Height / 2));
            drawingContext.DrawLine(pen2, new Point(this.AdornedElement.RenderSize.Width / 2, PositionY), new Point(this.AdornedElement.RenderSize.Width / 2, PositionY +Length));




        }


    }
}
